#include "../global/QmlNames.h"
#include "../util/Singleton.h"

#include <QQmlEngine>
#include <QSqlQuery>
#include <QSqlError>

#include "../persist/DbManager.h"
#include "SessionsModel.h"

X_BEGIN_NAMESPACE

// 数据角色属性名称，注意与ROLE_NAMES数组一致
enum ROLE_NAMES {
    id,
    sessionType,
    name,
    photo,
    lastMessageContent,
    lastMessageContentType,
    lastSenderName,
    lastSenderJid,
    lastMessageTime,
    pinToTop,
    showInList,
    DATA_ROLE_MAX
};

const char* SessionsModel::ROLE_NAMES[] = {
    "id",
    "sessionType",
    "name",
    "photo",
    "lastMessageContent",
    "lastMessageContentType",
    "lastSenderName",
    "lastSenderJid",
    "lastMessageTime",
    "pinToTop",
    "showInList"
};
const char SessionsModel::READ_SESSIONS[] = "select id, session_type, pin_to_top, show_in_list from sessions s order by s.pin_to_top desc, s.last_message_time desc, s.id asc";
const char SessionsModel::INSERT_SESSION[] = "insert or replace into sessions(id, session_type) values(:id, :session_type)";
const char SessionsModel::UPDATE_SESSION_LAST_ACTIVE_TIME[] = "update sessions set last_message_time = :last_message_time where id = :id";

SessionsModel::SessionsModel(QObject *parent)
    : QAbstractListModel(parent)
{
    for(int i = id; i < DATA_ROLE_MAX; ++i){
        m_roleNames.insert(i, ROLE_NAMES[i]);
    }
}

SessionsModel &SessionsModel::instance()
{
    return *Singleton<SessionsModel>::instance(SessionsModel::createInstance);
}

const char *SessionsModel::qmlName()
{
    return QmlNames::TopLevelModelSingleton::SessionsSingleton;
}

QObject *SessionsModel::qmlInstance(QQmlEngine *engine, QJSEngine *jsEngine)
{
    Q_UNUSED(engine);
    Q_UNUSED(jsEngine);

    return &instance();
}

int SessionsModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return m_sessions.size();
}

QVariant SessionsModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid()) return QVariant();

    SessionModel* session = m_sessions[index.row()];
    switch (role) {
        case id:
            return session->id();
        case sessionType:
            return (int)session->sessionType();
        case name:
            return session->name();
        case photo:
            return session->photo();
        case lastMessageContent:
            return session->lastMessageContent();
        case lastMessageContentType:
            return (int)session->lastMessageContentType();
        case lastSenderName:
            return session->lastSenderName();
        case lastSenderJid:
            return session->lastSenderJid();
        case lastMessageTime:
            return session->lastMessageTime();
        case pinToTop:
            return session->pinToTop();
        case showInList:
            return session->showInList();
        default:
            return QVariant("Not Implemented.");
    }
}

QHash<int, QByteArray> SessionsModel::roleNames() const
{
    return m_roleNames;
}

SessionsModel *SessionsModel::createInstance()
{
    SessionsModel* inst = new SessionsModel(NULL);
    return inst;
}

/**
 * @brief 会话排序比较器。
 *
 * 比较逻辑：
 * 1 会话被置顶时，需要排在前面。
 * 2 非置顶会话根据最后的消息时间排序
 *
 * @param v1
 * @param v2
 * @return
 */
bool SessionsModel::sessionLessThan(const SessionModel *s1, const SessionModel *s2)
{
    if(s1->pinToTop() && !s2->pinToTop()) return false;
    if(!s1->pinToTop() && s2->pinToTop()) return true;

    return s1->lastMessageTime() > s2->lastMessageTime();
}

bool SessionsModel::sessionGreaterThan(const SessionModel *s1, const SessionModel *s2)
{
    return !sessionLessThan(s1, s2);
}

void SessionsModel::loadDataFromDb()
{
    try{
        // 再加载
        QSqlQuery query = DbManager::instance().createQuery();

        query.exec(READ_SESSIONS);
        while (query.next()) {
            // id, session_type, pin_to_top, show_in_list
            QString sessionId = query.value("id").toString();
            SessionType sessionType = SessionType(query.value("session_type").toInt());
            bool pinToTop = query.value("pin_to_top").toBool();
            bool showInList = query.value("show_in_list").toBool();

            SessionModel& session = addSession(sessionId, sessionType, false);
            session.setPinToTop(pinToTop)
                    .setShowInList(showInList);

            // 创建完成后连接会话信号
            QObject::connect(&session, SIGNAL(lastMessageTimeChanged(QString, QDateTime)),
                             this, SLOT(onSessionLastMessageTimeChanged(QString, QDateTime)));
        }

        beginInsertRows(QModelIndex(), 0, m_sessions.size() - 1);
        endInsertRows();
    } catch(...){
        qDebug() << "Load sessions from database failed.";
    }
}

/**
 * @brief 更新会话消息的最后时间。
 *
 * 这个时间应该仅用于会话数据首次加载，并在有会话消息发生时，更新最后时间，
 * 不应该用于会话消息的最后时间。
 */
void SessionsModel::updateSessionLastMessageActiveTime(const QString& sessionId)
{
    QSqlQuery query =  DbManager::instance().createQuery();
    query.prepare(UPDATE_SESSION_LAST_ACTIVE_TIME);
    query.bindValue(":last_message_time", QDateTime::currentDateTime());
    query.bindValue(":id", sessionId);
    if(!query.exec()){
        qDebug() << query.lastError().text();
    }
}

void SessionsModel::houseKeep()
{
    beginRemoveRows(QModelIndex(), 0, m_sessions.size() - 1);

    foreach (SessionModel* session, m_sessions) {
        delete session;
    }

    m_sessions.clear();
    m_sessionsById.clear();

    endRemoveRows();
}

int SessionsModel::sessionIndex(const QString &id) const
{
    if(!m_sessionsById.contains(id)) return -1;

    return m_sessions.indexOf(m_sessionsById[id]);
}

QObject* SessionsModel::findSession(const QString &id) const
{
    QMap<QString, SessionModel*>::const_iterator it = m_sessionsById.find(id);

    SessionModel* session = it.value();
    return session;
}

/**
 * @brief 增加一个新的会话
 *
 * @param id
 * @param type
 * @param reorder
 * @return
 */
SessionModel& SessionsModel::addSession(const QString &id, SessionType sessionType, bool reorder)
{
    SessionModel* session = new SessionModel(id);
    session->setSessionType(sessionType);
    m_sessionsById[id] = session;

    bool inserted = false;

    // 排序比较耗时，可选择是否重新排序
    if(reorder){
        for(int i = 0; i < m_sessions.size(); ++i){
            if(sessionGreaterThan(m_sessions[i], session)) continue;

            m_sessions.insert(i, session);
            inserted = true;
            break;
        }
    }

    if(!inserted){
        m_sessions.push_back(session);
    }

    return *session;
}

/**
 * @brief 创建并保存会话到数据库
 *
 * @param id
 * @param type
 * @return
 */
SessionModel &SessionsModel::createSession(const QString &id, SessionType sessionType)
{
    // 存在直接返回
    if(m_sessionsById.contains(id)){
        emit sessionAdded(id);

        return *m_sessionsById[id];
    }

    // 保存到数据库
    QSqlQuery query = DbManager::instance().createQuery(INSERT_SESSION);
    query.prepare(INSERT_SESSION);
    query.bindValue(":id", id);
    query.bindValue(":session_type", (int)sessionType);
    query.exec();

    if(query.lastError().isValid()){
        qDebug() << "Create session failed: " << query.lastError();
    }

    // 插入到列表
    SessionModel &session = addSession(id, sessionType);

    // 通知列表更新数据
    int index = m_sessions.indexOf(&session);
    beginInsertRows(QModelIndex(), index, index);
    endInsertRows();

    // 定位至新创建的会话
    emit sessionAdded(id);

    return session;
}

/**
 * @brief 处理会话最后消息改变事件。
 *
 * 对会话重新排序，更新数据库的最后消息时间。
 *
 * @param time
 */
void SessionsModel::onSessionLastMessageTimeChanged(const QString& sessionId, QDateTime time)
{
    Q_UNUSED(time);

    // 更新数据库
    updateSessionLastMessageActiveTime(sessionId);

    // 排序
    std::sort(m_sessions.begin(), m_sessions.end(), SessionsModel::sessionLessThan);

    // 通知数据已经修改
    QModelIndex topLeftIndex = createIndex(0, 0, (void *)NULL);
    QModelIndex bottomRightIndex = createIndex(m_sessions.size(), 0, (void *)NULL);
    emit dataChanged(topLeftIndex, bottomRightIndex);
}

X_END_NAMESPACE
